#include "rtmp_play.h"
#include "rtmp_sdk.h"
#include "rtmp.h"
#include "../utils.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdio.h>
#include "list.h"
#include "rtmp_sys.h"
#include "log.h"
void write_log(const char * strInfo, ...);
void write_event(int64_t handle,char* str,int type,int param);
extern int  bOpen_socket5;
typedef struct RTMP_PLAY_MGR_
{
	pthread_mutex_t rtmp_mutex ;
	pthread_t th;
	RTMP* r;
	int bConnected;
	char rtmp_url[2048];
	unsigned char bStop;


	char sps[2048];
	int  sps_len;
	char pps[2048];
	int  pps_len;
	char vps[2048];
	int  vps_len;
	int  nWidth;
	int  nHeight;

	int nSampleRate;
	int nChannel;
	int nAuidoType;
	void* param;
	int nRecvTime;
	int nFrameCount;
	int nRecvAllSize;
	int video_type;

} RTMP_PLAY_MGR;
typedef struct RTMP_PLAY_MSG
{
	RTMPPacket packet;
	int64_t  nTimeStamp;
	uint8_t  bkey;
} RTMP_PLAY_MSG;
fRealCallBackVideo RealCallBackVideo = 0;
fRealCallBackAudio RealCallBackAudio = 0;

void CallBackVideo(void* param,int64_t handle,char* data,int datalen,int nWidth,int nHeight,int bkey,int nTimeStamp,int video_type)
{
	if (RealCallBackVideo)
	{
		RealCallBackVideo(param,handle,data,datalen,nWidth,nHeight,bkey,nTimeStamp,video_type);
	}
}
void CallBackAudio(void* param,int64_t handle,char* data,int datalen,int nSampleRate,int nChannel,int nAudioType,int nTimeStamp)
{
	if (RealCallBackAudio)
	{
		RealCallBackAudio(param,handle,data,datalen,nSampleRate,nChannel,nAudioType,nTimeStamp);
	}
}
void RtmpPlayParamInit(RTMP_PLAY_MGR* mgr)
{
	if (mgr->r == 0)
	{
		mgr->r = RTMP_Alloc();
		RTMP_Init(mgr->r);
		mgr->r->pRtmpPlayMgr = (void*)mgr;
	}
	mgr->bConnected = 0;
}
void RtmpPlayParamFree(RTMP_PLAY_MGR* mgr)
{
	if(mgr&&mgr->r)
	{
		RTMP_Close(mgr->r);
		RTMP_Free(mgr->r);
		mgr->r = 0;
	}
}
void HandleVideoHeaders_H265(RTMP_PLAY_MGR* mgr,  char* pvideo,int datalen)
{
	char* start,*end,*cur;
	int nWidth = 0,nHeight = 0;
	char naltype;
	char start_code[] = {0x00,0x00,0x00,0x01};
	start = cur =  pvideo + 28;
	end = pvideo + datalen - 28;
	if (datalen < 28)
	{
		return;
	}
	naltype = *start;
	if (naltype == 0x20)
	{
		int vpscount = get_byte16(start + 1);
		mgr->vps_len = get_byte16(start + 3);
		memcpy(mgr->vps ,start_code ,4);
		memcpy(mgr->vps + 4,start + 5,mgr->vps_len);
		start += (mgr->vps_len + 5);
		mgr->vps_len += 4;
	}
	naltype = *start;
	if (naltype == 0x21)
	{
		int spscount = get_byte16(start + 1);
		mgr->sps_len = get_byte16(start + 3);
		memcpy(mgr->sps ,start_code ,4);
		memcpy(mgr->sps + 4,start + 5,mgr->sps_len);
		start += (mgr->sps_len + 5);
		mgr->sps_len += 4;
	}
	naltype = *start;
	if (naltype == 0x22)
	{
		int ppscount = get_byte16(start + 1);
		mgr->pps_len = get_byte16(start + 3) ;
		memcpy(mgr->pps ,start_code ,4);
		memcpy(mgr->pps + 4,start + 5,mgr->pps_len);
		start += (mgr->pps_len + 5);
		mgr->pps_len += 4;
	}


}
void HandleVideoHeaders(RTMP_PLAY_MGR* mgr,  char* pvideo,int datalen)
{
	char* start,*end,*cur;
	int nWidth = 0,nHeight = 0;
	char start_code[] = {0x00,0x00,0x00,0x01};
	start = cur =  pvideo;
	end = pvideo + datalen;

	if (end - cur <= 13)
	{
		return;
	}
	cur += 11;	
	mgr->sps_len = get_byte16(cur);
	cur += 2;

	if (end - cur <= mgr->sps_len)
	{
		return;
	}
	memcpy(mgr->sps ,start_code ,4);
	memcpy(mgr->sps + 4,cur,mgr->sps_len);
	cur += mgr->sps_len;
	mgr->sps_len += 4;
	if (end - cur <= 3)
	{
		return;
	}
	cur += 1;

	mgr->pps_len = get_byte16(cur);
	if (end - cur <= mgr->pps_len)
	{
		return;
	}
	memcpy(mgr->pps ,start_code ,4);
	memcpy(mgr->pps + 4,cur + 2,mgr->pps_len);
	mgr->pps_len += 4;

	parse_sps((char*)mgr->sps + 4,mgr->sps_len - 4,&nWidth,&nHeight);
	mgr->nWidth = nWidth;
	mgr->nHeight = nHeight;
	LOG_INFO("HandleVideoHeaders nWidth:%d  nHeight:%d spslen:%d ppslen:%d",nWidth,nHeight,mgr->pps_len,mgr->sps_len);

}
int GetSoundRate(int nSoundRateIndex)
{
	int nSampleRate=  8000;
	switch (nSoundRateIndex)
	{
	case 0x00:
		nSampleRate = 8000 ;	
		break;
	case 0x01 :
		nSampleRate = 16000;	
		break;
	case 0x02 :
		nSampleRate = 22050;	
		break;
	case 0x03 :
		nSampleRate = 44100;	
		break;
	}
	return nSampleRate;

}
int GetSoundSampleByIndex_(int nSoundRateIndex)
{
	int nSampleRate= 0x07;
	switch (nSoundRateIndex)
	{
	case 0x00:
		nSampleRate = 96000 ;	
		break;
	case 0x01 :
		nSampleRate = 88200;	
		break;
	case 0x02 :
		nSampleRate = 64000;	
		break;
	case 0x03 :
		nSampleRate = 48000;	
		break;
	case 0x04 :
		nSampleRate = 44100;	
		break;
	case 0x05 :
		nSampleRate = 32000;	
		break;
	case 0x06 :
		nSampleRate = 24000;	
		break;
	case 0x07:
		nSampleRate =  22050;	
		break;
	case 0x08 :
		nSampleRate = 16000;	
		break;
	case 0x09 :
		nSampleRate = 12000;	
		break;
	case 0x0A :
		nSampleRate = 11025;	
		break;
	case 0x0B :
		nSampleRate = 8000;	
		break;
	case 0x0C :
		nSampleRate = 7350 ;	
		break;
	}
	return nSampleRate;
}
void HandleAudio(RTMP *r, const RTMPPacket *packet)
{
	if (packet&&r)
	{
		RTMP_PLAY_MGR* mgr = (RTMP_PLAY_MGR*)r->pRtmpPlayMgr;
		char* audiodata = packet->m_body;
		int datalen = packet->m_nBodySize;
		if (datalen <= 2)
		{
			return;
		}
		if (  mgr->nSampleRate == 0)
		{
			unsigned char audioTypeTmp = *(audiodata);
			mgr->nAuidoType = audioTypeTmp>>4;
			if (mgr->nAuidoType == 10)
			{	
				if (*(audiodata + 1) == 0)
				{
					if (mgr->nSampleRate  == 0)
					{
						unsigned char audioTypeTmp1 = *(audiodata + 2);
						unsigned char audioTypeTmp2 = *(audiodata + 3);
						unsigned char nindex = ((audioTypeTmp1&0x07)<<1) |(audioTypeTmp2 >>7);
						mgr->nSampleRate = GetSoundSampleByIndex_(nindex);
						if (mgr->r->m_nChannels != 0)
						{
							mgr->nChannel = mgr->r->m_nChannels;
						}
						else
						{
							mgr->nChannel = (audioTypeTmp2>>3)&0xF;
						}
						// 						if (datalen >= 8)
						// 						{
						// 							uint32_t  state = get_byte24(audiodata + 4);						
						// 							if ((state & LATM_MASK) == LATM_HEADER)
						// 							{
						// 								mgr->nAuidoType = RTMP_CODECID_AAC_LATM;							
						// 							}
						// 						}

						LOG_INFO("get aac header nSampleRate:%d  nChannel:%d audioType:%d",mgr->nSampleRate,mgr->nChannel,mgr->nAuidoType);


					}
				}
			}
			else
			{
				mgr->nSampleRate = GetSoundRate((audioTypeTmp&0x0F)>>2);
				mgr->nChannel = ((audioTypeTmp&0x01) == 0x01) ? 2:1;
				//m_bBit16 = ( (audioTypeTmp&0x02) ==0x02) ? 1:0;
				//LOG_INFO("HandleAudio nSampleRate:%d  nChannel:%d audioType:%d",mgr->nSampleRate,mgr->nChannel,mgr->nAuidoType);
			}
			if (mgr->nSampleRate == 0)
			{
				mgr->nSampleRate = mgr->r->m_nSampleRate;
				mgr->nChannel = mgr->r->m_nChannels;
				if (mgr->nChannel == 0)
				{
					if (mgr->r->m_bStereo)
					{
						mgr->nChannel = 2;
					}
					else
					{
						mgr->nChannel = 1;
					}
				}
			}
			if (mgr->nSampleRate != mgr->r->m_nSampleRate && mgr->r->m_nSampleRate != 0)
			{
				//mgr->nSampleRate = mgr->r->m_nSampleRate;
			}
			if (mgr->nChannel != mgr->r->m_nChannels && mgr->r->m_nChannels != 0)
			{
				mgr->nChannel = mgr->r->m_nChannels;
			}
			return ;
		}

		if (mgr->nAuidoType == 10 || mgr->nAuidoType == 11)
		{
			if(*(audiodata + 1) == 1)
			{
				CallBackAudio(mgr->param,(int64_t)mgr,audiodata + 2,datalen - 2,mgr->nSampleRate,mgr->nChannel,mgr->nAuidoType,packet->m_nTimeStamp);
			}

		}
		else
		{
			CallBackAudio(mgr->param,(int64_t)mgr,audiodata + 1,datalen - 1,mgr->nSampleRate,mgr->nChannel,mgr->nAuidoType,packet->m_nTimeStamp);
		}



	}

	//printf("HandleAudio %d\n",packet->m_nBodySize); 
}
int ParseFlvToH264Nal( char* nalsbuf, int nalsize,int* bneedparam,int bkye)
{
	char h264Statecode[] = {0x00,0x00,0x00,0x01};
	char* pnalcur = nalsbuf;
	*bneedparam = 1;
	while(nalsize >= 4)
	{
		unsigned int size = get_byte32(pnalcur);
		int ntype = 0;
		memcpy(pnalcur,h264Statecode,4);
		ntype = (*(pnalcur+4))&0x1f;
		if(ntype == 0x07 || ntype == 0x08||ntype == 0x01)
		{
			*bneedparam = 0;
		}
		if (ntype == 0x05 ||ntype == 0x07 || ntype == 0x08)
		{
			bkye = 1;
		}

		if (nalsize <= size + 4)
		{
			return 0;
		}	
		nalsize -= (size + 4);
		pnalcur += (size + 4);

	}
	return 1;
}
int ParseFlvToH265Nal( char* nalsbuf, int nalsize,int bkye,int* bneedspspps)
{
	char h264Statecode[] = {0x00,0x00,0x00,0x01};
	char* pnalcur = nalsbuf;
	*bneedspspps = 1;
	while(nalsize >= 4)
	{
		unsigned int size = get_byte32(pnalcur);
		memcpy(pnalcur,h264Statecode,4);
		if (bkye)
		{
			unsigned char nalType = *(pnalcur + 4);
			nalType = (nalType>>1)&0x3F;
			if (nalType == 0x20 || nalType == 0x21 ||nalType == 0x22)
			{
				*bneedspspps = 0;
			}

		}
		else
		{
			*bneedspspps = 0;
		}
		if (nalsize <= size + 4)
		{
			return 0;
		}	
		nalsize -= (size + 4);
		pnalcur += (size + 4);

	}
	return 1;
}

void HandleVideo(RTMP *r, const RTMPPacket *packet)
{
	if (packet&&r)
	{
		RTMP_PLAY_MGR* mgr = (RTMP_PLAY_MGR*)r->pRtmpPlayMgr;
		char* pdata = packet->m_body;
		int bkey = 0;
		if (*pdata == 0x17 || *pdata == 0x19)
		{
			if (mgr->video_type == 0)
			{
				mgr->video_type = (*pdata)&0x0f;
			}
			bkey = 1;
		}

		if (time(NULL) - mgr->nRecvTime > 0)
		{
			write_event((int64_t)mgr,"RTMP_PLAY_FRAMERATE",RTMP_EVENT_RTMP_PLAY_FRAMERATE,mgr->nFrameCount);
			write_event((int64_t)mgr,"RTMP_PLAY_BITRATE",RTMP_EVENT_RTMP_PLAY_BITRATE,mgr->nRecvAllSize/1024);
			mgr->nRecvTime  = time(NULL);
			mgr->nFrameCount = 0;
			mgr->nRecvAllSize = 0;
		}
		mgr->nFrameCount++;
		mgr->nRecvAllSize += packet->m_nBodySize;
		if (bkey&&*(pdata + 1) == 0)
		{
			if (mgr->video_type == VIDEO_H264)
			{
				HandleVideoHeaders(mgr,pdata,packet->m_nBodySize);
			}
			else
			{
				mgr->nWidth = mgr->r->m_nWidth;
				mgr->nHeight = mgr->r->m_nHeight;
				HandleVideoHeaders_H265(mgr,pdata,packet->m_nBodySize);
			}
			
		}
		else
		{
			int b_need_sps_pps_ = 0;
			int nH264Size = packet->m_nBodySize - 5;
			if (mgr->video_type == VIDEO_H264)
			{

				//ParseFlvToH264Nal(pdata + 5,nH264Size,&b_need_sps_pps_,bkey);
				if (b_need_sps_pps_)
				{
					int allsize = nH264Size + mgr->sps_len + mgr->pps_len;
					char* buf = (char*)malloc(allsize);
					memcpy(buf,mgr->sps,mgr->sps_len);
					memcpy(buf + mgr->sps_len,mgr->pps,mgr->pps_len);	
					memcpy(buf + mgr->sps_len + mgr->pps_len,pdata + 5, nH264Size);
					nH264Size += (mgr->sps_len + mgr->pps_len);

					CallBackVideo(mgr->param,(int64_t)mgr,buf,nH264Size,mgr->nWidth,mgr->nHeight,bkey,packet->m_nTimeStamp,mgr->video_type);
					free(buf);

				}
				else
				{
					CallBackVideo(mgr->param,(int64_t)mgr,pdata + 5,nH264Size,mgr->nWidth,mgr->nHeight,bkey,packet->m_nTimeStamp,mgr->video_type);
				}

			}
			else
			{
				ParseFlvToH265Nal(pdata + 5,nH264Size,bkey,&b_need_sps_pps_);
				LOG_INFO("write size :%d",packet->m_nBodySize);
				if (b_need_sps_pps_)
				{
					int allsize = nH264Size + mgr->sps_len + mgr->pps_len + mgr->vps_len + 1000;
					char* buf = (char*)malloc(allsize);
					memcpy(buf,mgr->vps,mgr->vps_len);
					memcpy(buf + mgr->vps_len,mgr->sps,mgr->sps_len);	
					memcpy(buf + mgr->vps_len + mgr->sps_len,mgr->pps,mgr->pps_len);
					memcpy(buf + mgr->vps_len + mgr->sps_len + mgr->pps_len,pdata + 5, nH264Size);
					nH264Size += (mgr->sps_len + mgr->pps_len + mgr->vps_len);

					
				
					CallBackVideo(mgr->param,(int64_t)mgr,buf,nH264Size,mgr->nWidth,mgr->nHeight,bkey,packet->m_nTimeStamp,mgr->video_type);
					free(buf);

				}
				else
				{
					CallBackVideo(mgr->param,(int64_t)mgr,pdata + 5,nH264Size,mgr->nWidth,mgr->nHeight,bkey,packet->m_nTimeStamp,mgr->video_type);
				}

			}
			

		}

	} 
}
void* playThread( void *arg )  
{  
	RTMP_PLAY_MGR* mgr = (RTMP_PLAY_MGR*)arg;
	if(mgr)
	{
		int recv_data_time_ = time(0);
		while(!mgr->bStop)
		{

			if (!mgr->bConnected)
			{
				RtmpPlayParamFree(mgr);
				RtmpPlayParamInit(mgr);
				mgr->nRecvTime = 0;
				mgr->nFrameCount = 0;
				mgr->nRecvAllSize = 0;
				write_event((int64_t)mgr,"RTMP_PLAY_FRAMERATE",RTMP_EVENT_RTMP_PLAY_FRAMERATE,mgr->nFrameCount);
				write_event((int64_t)mgr,"RTMP_PLAY_BITRATE",RTMP_EVENT_RTMP_PLAY_BITRATE,mgr->nRecvAllSize/1024);
				if(RtmpPlayConnect((int64_t)arg,mgr->rtmp_url) == RTMP_SUCCESS)
				{
					write_event((int64_t)mgr,"RtmpPlayConnect SUCCESS",RTMP_EVENT_RTMP_PLAY_CONNECT_SUCCESS,0);
					LOG_INFO("RtmpPlayConnect %s  sucess",mgr->rtmp_url);
				}
				else
				{
					if (bOpen_socket5)
					{
						write_event((int64_t)mgr,"RtmpPlayConnect Proxy FAILED",RTMP_EVENT_RTMP_PLAY_CONNECT_PROXY_FAILED,0);
					}
					else
					{
						write_event((int64_t)mgr,"RtmpPlayConnect FAILED",RTMP_EVENT_RTMP_PLAY_CONNECT_FAILED,0);
					}
					
					LOG_INFO("RtmpPlayConnect %s  failed",mgr->rtmp_url);
                    msleep(200);
				}
			}
			else
			{
				RTMPPacket packet = { 0 };

				while (!mgr->bStop&&RTMP_IsConnected(mgr->r) && RTMP_ReadPacket(mgr->r, &packet))
				{
					if (RTMPPacket_IsReady(&packet))
					{
						if (!packet.m_nBodySize)
							continue;

						RTMP_ClientPacket(mgr->r, &packet);
						RTMPPacket_Free(&packet);
					}
				}		

				if (!RTMP_IsConnected(mgr->r) || RTMP_IsTimedout(mgr->r) )
				{
					msleep(100);
					mgr->bConnected = 0;
					write_event((int64_t)mgr,"RtmpPlayConnect socket close",RTMP_EVENT_RTMP_PLAY_SOCKET_CLOSE,0);
				}

			}


		}	
		pthread_mutex_destroy(&mgr->rtmp_mutex); 
		RtmpPlayParamFree(mgr);
		free(mgr);
	}
	return 0;  
}  

int64_t RtmpPlayInit(char* rtmp_url,void* param)
{
	int thread_ret = 0;  
	int64_t ret  = 0;
	RTMP_PLAY_MGR* mgr = (RTMP_PLAY_MGR*)malloc(sizeof( RTMP_PLAY_MGR));
	ret = (int64_t)mgr;  
	memset(mgr,0,sizeof(RTMP_PLAY_MGR));
	strcpy(mgr->rtmp_url,rtmp_url);
	pthread_mutex_init(&mgr->rtmp_mutex,NULL);
	mgr->bConnected = 0;
	mgr->bStop = 0;
	mgr->param = param;
	mgr->nRecvTime = 0;
	mgr->nRecvAllSize = 0;
	thread_ret = pthread_create( &mgr->th, 0,playThread, mgr );  
	return ret;
}

void RtmpPlayFree(int64_t handle)
{
	RTMP_PLAY_MGR* mgr = (RTMP_PLAY_MGR*)handle;
	if(mgr != 0)
	{	
		mgr->bStop = 1;
	}	
}


int RtmpPlayConnect(int64_t handle,char* url)
{
	RTMP_PLAY_MGR* mgr = (RTMP_PLAY_MGR*)handle;
	RTMP* rtmp = (RTMP*)mgr->r;
	if (rtmp == 0)
	{
		return RTMP_HANDLE_INVALID;
	}
	if(!url)
	{
		return RTMP_HANDLE_INVALID;
	}
	if(!RTMP_SetupURL(rtmp,url))
	{
		return RTMP_URL_INVALID;
	}

	rtmp->Link.swfUrl.av_len = rtmp->Link.tcUrl.av_len;
	rtmp->Link.swfUrl.av_val = rtmp->Link.tcUrl.av_val;
	rtmp->Link.flashVer.av_val = "FMLE/3.0 (compatible; FMSc/1.0)";
	rtmp->Link.flashVer.av_len = (int)strlen(rtmp->Link.flashVer.av_val);

	if (rtmp->Link.isIpv6)
	{
		if(!RTMP_Connect_Ipv6(rtmp, NULL))
		{
			return RTMP_CONNETC_FAILED;
		}
	}
	else
	{
		if(!RTMP_Connect(rtmp, NULL))
		{
			return RTMP_CONNETC_FAILED;
		}

	}


	if (!RTMP_ConnectStream(rtmp,0))
	{
		return RTMP_CONNETC_STREAM_FAILED;
	}
	pthread_mutex_lock(&mgr->rtmp_mutex);
	mgr->bConnected = 1;
	pthread_mutex_unlock(&mgr->rtmp_mutex);

	return RTMP_SUCCESS;

}
void SetRealCallBack(fRealCallBackVideo RealCallBackVideo_,fRealCallBackAudio RealCallBackAudio_)
{
	RealCallBackVideo = RealCallBackVideo_;
	RealCallBackAudio = RealCallBackAudio_;
}
void get_rtmp_play_sps_pps(int64_t handle,char* sps,int* spslen,char* pps,int* ppslen)
{
	RTMP_PLAY_MGR* mgr = (RTMP_PLAY_MGR*)handle;
	if (mgr)
	{
		*spslen = *ppslen = 0;
		if (mgr->sps_len > 0)
		{
			memcpy(sps,mgr->sps,mgr->sps_len);
			*spslen = mgr->sps_len;
		}
		if (mgr->pps > 0)
		{
			memcpy(pps,mgr->pps,mgr->pps_len);
			*ppslen = mgr->pps_len;
		}
	}

}
